home *** CD-ROM | disk | FTP | other *** search
- /*********************************************************************
-
- files.c
-
- This file contains the file I/O code for the QuickDraw GX aware
- sample, "Simple Sample GX."
-
- Additional info can be found in the related develop #19 article,
- "Adding QuickDraw GX Printing to QuickDraw Applications."
-
- Dave Hersey, Apple Developer Technical Support.
-
- ——————— Edit Trail ———————
-
- hatched: 1/22/94 - dmh
- cleaned up for 2nd draft of develop article: 3/10/94 - dmh
- cleaned up for final: 4/14/94 - dmh
- universalized: 8/25/94 - dmh
-
- *********************************************************************/
-
- #include "Simple Sample.h"
-
-
- /************************************************************
- MyLoadDocument - This routine loads a previously saved
- document.
-
- *************************************************************/
-
- OSErr MyLoadDocument(MyDocumentPtr whichDocument)
- {
- OSErr err;
- short oldResFile, dataRefNum = -1, resRefNum = -1;
- StandardFileReply sfReply;
- SFTypeList myTypeList;
-
- // Let the user select a document to open.
-
- myTypeList[0] = kMyDocType;
- StandardGetFile(nil, 1, myTypeList, &sfReply);
- require_action(sfReply.sfGood, UserHasCancelled, err = iPrAbort;);
-
- /*
- Make sure that we haven't already opened this document. If
- we have, just bring the old window forward.
- */
- nrequire_action(MyIsWindowAlreadyOpen(&sfReply.sfFile),
- DocIsAlreadyOpen, err = iPrAbort;);
-
- // Open the selected file's data fork and resource fork.
-
- err = FSpOpenDF(&sfReply.sfFile, fsRdWrPerm, &dataRefNum);
- nrequire(err, CouldNotOpenDataFork);
-
- resRefNum = HOpenResFile(sfReply.sfFile.vRefNum, sfReply.sfFile.parID,
- sfReply.sfFile.name, fsRdPerm);
-
- err = ResError();
- nrequire(err, CouldNotOpenResourceFork);
-
- /*
- If we're successful in opening the file, set our document's
- FSSpec info, its title, and its window's title.
- */
-
- oldResFile = CurResFile();
-
- BlockMove(&sfReply.sfFile, &whichDocument->documentFSSpec, sizeof(FSSpec));
- BlockMove(&sfReply.sfFile.name, whichDocument->documentTitle,
- (long) sfReply.sfFile.name[0] +1);
-
- SetWTitle(whichDocument->documentWindow, whichDocument->documentTitle);
- err = MyLoadPrintInfo(whichDocument, resRefNum);
-
- // Now load the data for our document's pages.
-
- whichDocument->numPages = MyLoadPageCount(resRefNum);
- whichDocument->curPage = 1;
-
- /*
- Place your application-specific code here to load
- other data associated with the document.
- .
- .
- .
- */
-
- MyAdjustMenus();
-
- // Close the data and resource forks of this document.
-
- UseResFile(oldResFile);
- CloseResFile(resRefNum);
-
- CouldNotOpenResourceFork:
- FSClose(dataRefNum);
-
- CouldNotOpenDataFork:
- DocIsAlreadyOpen:
- UserHasCancelled:
- return err;
- }
-
-
- /************************************************************
- MyFSLoadDocument - This routine opens a document and loads
- its previously saved job, reassociating any formats that
- the job contains. It's just like MyLoadDocument, but it
- opens the indicated file and doesn't present a file
- dialog.
-
- *************************************************************/
-
- OSErr MyFSLoadDocument(MyDocumentPtr whichDocument, FSSpec *docFSSpec,
- Boolean forPrinting)
- {
- OSErr err;
- short oldResFile, dataRefNum, resRefNum;
-
- /*
- Unless we're printing, make sure that we haven't already opened
- this document. If we have, just bring the old window forward.
- */
-
- require_action(forPrinting || !MyIsWindowAlreadyOpen(docFSSpec),
- DocIsAlreadyOpen, err = iPrAbort;);
-
- // Open the selected file's data fork and resource fork.
-
- err = FSpOpenDF(docFSSpec, fsRdWrPerm, &dataRefNum);
- nrequire(err, CouldNotOpenDataFork);
-
- resRefNum = HOpenResFile(docFSSpec->vRefNum, docFSSpec->parID, docFSSpec->name, fsRdPerm);
- err = ResError();
- nrequire(err, CouldNotOpenResourceFork);
-
- /*
- If we're successful in opening the file, set our document's
- FSSpec info and its window's title.
- */
-
- oldResFile = CurResFile();
-
- BlockMove(docFSSpec, &whichDocument->documentFSSpec, sizeof(FSSpec));
- BlockMove(docFSSpec->name, whichDocument->documentTitle, (long) docFSSpec->name[0] +1);
- SetWTitle(whichDocument->documentWindow, whichDocument->documentTitle);
-
- err = MyLoadPrintInfo(whichDocument, resRefNum);
-
- // Now load the data for our document's pages.
-
- whichDocument->numPages = MyLoadPageCount(resRefNum);
- whichDocument->curPage = 1;
-
- /*
- Place your application-specific code here to load
- other data associated with the document.
- .
- .
- .
- */
-
- MyAdjustMenus();
-
- // Close the data and resource forks of this document.
-
- UseResFile(oldResFile);
- CloseResFile(resRefNum);
-
- CouldNotOpenResourceFork:
- FSClose(dataRefNum);
-
- CouldNotOpenDataFork:
- DocIsAlreadyOpen:
- return err;
- }
-
-
- /************************************************************
- MyIsWindowAlreadyOpen - This routine compares a file spec
- with those for our currently opened documents. If we find
- a match, we bring that document's window forward and return
- true. Otherwise, we return false.
-
- *************************************************************/
-
- Boolean MyIsWindowAlreadyOpen(FSSpec *whichFSSpec)
- {
- WindowPtr curWindow;
- MyDocumentPtr curDocument;
- Boolean isAlreadyOpen = false;
-
- /*
- Make sure that we haven't already opened this document. If
- we have, just bring the old window forward and adjust our menus.
- */
- curWindow = FrontWindow();
-
- while (curWindow != nil)
- {
- if (((WindowPeek) curWindow)->windowKind == userKind)
- {
- curDocument = MyGetDocPtr(curWindow);
-
- isAlreadyOpen =
- ((curDocument->documentFSSpec.vRefNum == whichFSSpec->vRefNum) &&
- (curDocument->documentFSSpec.parID == whichFSSpec->parID) &&
- (IUEqualString(curDocument->documentFSSpec.name, whichFSSpec->name) == 0));
-
- if (isAlreadyOpen)
- {
- SelectWindow(curWindow);
- MyAdjustMenus();
- }
-
- nrequire(isAlreadyOpen, DocIsAlreadyOpen);
- }
-
- curWindow = (WindowPtr) ((WindowPeek) curWindow)->nextWindow;
- }
-
- DocIsAlreadyOpen:
- NoNeedToCheck:
- return isAlreadyOpen;
- }
-
-
- /************************************************************
- MySaveDocument - This routine saves a document and its
- corresponding job to disk. It also saves a format
- collection item containing our page-to-format
- correpondences.
-
- *************************************************************/
-
- OSErr MySaveDocument(MyDocumentPtr whichDocument, Boolean doingSaveAs)
- {
- OSErr err = noErr;
- short dataRefNum, oldResFile, resRefNum;
- FSSpec *docFSSpec;
- StandardFileReply sfReply;
- FInfo docFInfo;
-
- oldResFile = CurResFile();
- docFSSpec = &whichDocument->documentFSSpec;
-
- /*
- If we're doing a "Save as…", display the StandardFile
- dialog and have the user select a place to save the file.
- */
-
- if (doingSaveAs)
- {
- StandardPutFile("\pSave document:", whichDocument->documentTitle, &sfReply);
- require(sfReply.sfGood, UserHasCancelled);
-
- /*
- If we're replacing an existing file, delete it. Create
- our new file and set its creator and type.
- */
- if (sfReply.sfReplacing)
- err = HDelete(sfReply.sfFile.vRefNum, sfReply.sfFile.parID, sfReply.sfFile.name);
-
- nrequire(err, CouldNotDeleteOldFile);
-
- HCreateResFile(sfReply.sfFile.vRefNum, sfReply.sfFile.parID, sfReply.sfFile.name);
- HGetFInfo(sfReply.sfFile.vRefNum, sfReply.sfFile.parID,
- sfReply.sfFile.name, &docFInfo);
-
- docFInfo.fdCreator = kMyDocCreator;
- docFInfo.fdType = kMyDocType;
-
- err = HSetFInfo(sfReply.sfFile.vRefNum, sfReply.sfFile.parID,
- sfReply.sfFile.name, &docFInfo);
-
- /*
- If we're successful in creating the file, set our document's
- FSSpec info, its title and its window's title.
- */
-
- if (err == noErr) err = ResError();
- nrequire(err, CouldNotSetFileInfo);
-
- BlockMove(&sfReply.sfFile, docFSSpec, sizeof(FSSpec));
- BlockMove(&sfReply.sfFile.name, whichDocument->documentTitle,
- (long) sfReply.sfFile.name[0] +1);
-
- SetWTitle(whichDocument->documentWindow, whichDocument->documentTitle);
- }
-
- // Open the file's data fork and resource fork.
-
- err = FSpOpenDF(docFSSpec, fsRdWrPerm, &dataRefNum);
- nrequire(err, CouldNotOpenDataFork);
-
- resRefNum = HOpenResFile(docFSSpec->vRefNum, docFSSpec->parID,
- docFSSpec->name, fsRdWrPerm);
- err = ResError();
- nrequire(err, CouldNotOpenResourceFork);
-
- err = MySavePrintInfo(whichDocument, resRefNum);
-
- // Now save the data for our document's pages.
-
- if (!err)
- err = MySavePageCount(whichDocument, resRefNum);
-
- /*
- Place your application-specific code here to save
- other data associated with the document.
- .
- .
- .
- */
-
- // Close the data and resource forks of this document.
-
- CloseResFile(resRefNum);
-
- CouldNotOpenResourceFork:
- FSClose(dataRefNum);
-
- CouldNotOpenDataFork:
- CouldNotSetFileInfo:
- CouldNotDeleteOldFile:
- UserHasCancelled:
- UseResFile(oldResFile);
- return err;
- }
-
-
- /************************************************************
- MySavePageCount - This routine saves a resource containing
- the number of pages in our document.
-
- *************************************************************/
-
- OSErr MySavePageCount(MyDocumentPtr whichDocument, short resRefNum)
- {
- OSErr err;
- Handle thePageCount, oldPageCount;
-
- UseResFile(resRefNum);
-
- // If there's an existing resource, delete it.
-
- thePageCount = NewHandle(sizeof(long));
- nrequire((err = MemError()), CouldNotCreateHandle);
-
- *(long *)*thePageCount = whichDocument->numPages;
- oldPageCount = Get1Resource(kMyPageCountType, kMyPageCountID);
-
- if (oldPageCount != nil)
- {
- RmveResource(oldPageCount);
- UpdateResFile(resRefNum);
- DisposHandle(oldPageCount);
- }
-
- // Add our new resource.
-
- AddResource(thePageCount, kMyPageCountType, kMyPageCountID, "\p");
- err = ResError();
- nrequire(err, CouldNotAddResource);
-
- WriteResource(thePageCount);
- UpdateResFile(resRefNum);
- ReleaseResource(thePageCount);
-
- CouldNotAddResource:
- CouldNotCreateHandle:
- return err;
- }
-
-
- /************************************************************
- MyLoadPageCount - This routine loads a resource containing
- the number of pages in our document, and returns the number
- of pages.
-
- *************************************************************/
-
- long MyLoadPageCount(short resRefNum)
- {
- Handle thePageCount;
- long numPages;
-
- UseResFile(resRefNum);
-
- // Get the page count. If we don't find a count, return 1.
-
- thePageCount = Get1Resource(kMyPageCountType, kMyPageCountID);
-
- if (thePageCount != nil)
- {
- numPages = *(long *)*thePageCount;
- ReleaseResource(thePageCount);
- }
- else
- numPages = 1;
-
- return numPages;
- }
-
-
- /************************************************************
- MySaveFormatRefs - This routine saves page format indices
- for a document. We'll store this info in the job's
- default format's collection. We use this information to
- "reconstruct" the document the next time we open it.
- This routine is called when we save a document, before we
- have flattened the document's job.
-
- *************************************************************/
-
- OSErr MySaveFormatRefs(MyDocumentPtr whichDocument)
- {
- OSErr err = noErr;
- Handle theFormatIdxList;
- Collection fmtCollection;
- gxFormat defaultFmt;
-
- if (whichDocument->numPages > 0)
- {
-
- // Get the job's default format's collection.
-
- defaultFmt = GXGetJobFormat(whichDocument->documentJob, 1);
- fmtCollection = GXGetFormatCollection(defaultFmt);
-
- /*
- Create a list of page-to-format correspondences for the current
- document. If there are no errors, add the item to the job's
- default format's collection for later retrieval.
- */
- err = MyCreateFormatIndexList(whichDocument, &theFormatIdxList);
-
- if (err == noErr)
- {
- HLock(theFormatIdxList);
- err = AddCollectionItem(fmtCollection,
- kMyFormatInfoType,
- kMyFormatInfoTagID,
- GetHandleSize(theFormatIdxList),
- *theFormatIdxList);
-
- DisposHandle(theFormatIdxList);
- }
- }
-
- return err;
- }
-
-
- /************************************************************
- MyCreateFormatIndexList - This routine stores the index of
- each page's format in a handle. The index of page #1's
- format will go in the first longword of theFormatIdxList
- handle, the index of the next page's format will go in
- the next longword, and so on. The handle is created and
- returned to the caller.
-
- *************************************************************/
-
- OSErr MyCreateFormatIndexList(MyDocumentPtr whichDocument, Handle *theFormatIdxList)
- {
- OSErr err;
- long fmtIdx, pg, *idxList;
- gxFormat curFormat;
-
- /*
- Create a handle large enough to hold all of our entries. We use
- NewHandleClear so that all of our indices are initialized to 0
- (an invalid format index). Since our application stores a nil
- format reference for any page which uses the job's default format,
- this allows us to indicate these "nil references" by an index of
- 0 in our collection item. When we re-open the document, we'll
- know that an index of 0 means "Use the job's default format."
- */
- *theFormatIdxList = NewHandleClear(sizeof(long) * (whichDocument->numPages));
- err = MemError();
-
- /*
- If there aren't any errors, go through every format in the document's
- job. If the format is used by any pages of our document, store the
- format's index in those page entries of theFormatIdxList. We skip
- format #1, since that's the job format (and we're storing nil for the
- index in this case). Because we created the handle with NewHandleClear,
- nil will already be stored in any entries we don't change.
- */
- if (err == noErr)
- {
- HLock(*theFormatIdxList);
- idxList = (long *) **theFormatIdxList;
-
- for (fmtIdx = 2; fmtIdx <= GXCountJobFormats(whichDocument->documentJob); fmtIdx++)
- {
- curFormat = GXGetJobFormat(whichDocument->documentJob, fmtIdx);
-
- for (pg = 1; pg <= whichDocument->numPages; pg++)
- if (whichDocument->pageFormat[pg -1] == curFormat)
- idxList[pg -1] = fmtIdx;
- }
-
- HUnlock(*theFormatIdxList);
- }
-
- return err;
- }
-
-
- /************************************************************
- MyAdjustFormats - This routine associates new format
- references with a document, based upon the format indices
- which were saved with the document. The routine is called
- when we open a document.
-
- The format references will be stored in the passed
- MyDocumentPtr structure.
-
- *************************************************************/
-
- OSErr MyAdjustFormats(MyDocumentPtr whichDocument)
- {
- OSErr err = noErr;
- Handle theFormatIdxList = nil;
- gxFormat theFormat, defaultFmt;
- long pg, numPages, fmtIdx, *idxList, idx, listSize, attribs;
- Collection fmtCollection;
-
- /*
- Get the job's default format's collection, and look for one
- of our page-to-format correspondence items in it.
- */
- defaultFmt = GXGetJobFormat(whichDocument->documentJob, 1);
- fmtCollection = GXGetFormatCollection(defaultFmt);
-
- /*
- Load our item containing our page-to-format correspondences.
- We do this in two passes. First we determine if the item
- exists, and if so, get its size. Next we create a handle
- to hold the item (if it exists), and then actually retrieve it.
- Because there will be one longword entry for each page of our
- document, we can determine the number of pages in the document.
- */
- err = GetCollectionItemInfo(fmtCollection, kMyFormatInfoType,
- kMyFormatInfoTagID, &idx, &listSize, &attribs);
-
- if (err == noErr)
- theFormatIdxList = NewHandle(listSize);
-
- if (theFormatIdxList != nil)
- {
- HLock(theFormatIdxList);
-
- err = GetCollectionItem(fmtCollection, kMyFormatInfoType,
- kMyFormatInfoTagID, dontWantSize, *theFormatIdxList);
-
- numPages = listSize / sizeof(long);
-
- /*
- Now load the data for our document's pages. (In this app, we cheat and
- rebuild the pages here, rather than load and save the page data.)
- */
- for (pg = 1; !err && (pg < numPages); pg++)
- {
- long newPage = pg -1;
-
- err = MyInsertPage(whichDocument, &newPage);
- whichDocument->curPage = pg;
- }
- whichDocument->curPage = 1;
-
- /*
- Place your application-specific code here to load
- page data associated with the document.
- .
- .
- .
- */
-
- /*
- Loop through each saved index. The way we saved them, the first
- is for page 1, the second is for page 2, and so on. We need to call
- GXGetJobFormat for each saved index. If the index is nil, that's
- just our way of saying "use the job format." In that case, we don't
- call GXGetJobFormat, we just store nil for the page's format. Store
- the format references as they're processed. When we're done, throw
- away the handle we created.
- */
- idxList = (long *) *theFormatIdxList;
-
- for (pg = 0; (err == noErr) && (pg < numPages); pg++)
- {
- fmtIdx = idxList[pg];
-
- if (fmtIdx != (long) nil)
- {
- theFormat = GXGetJobFormat(whichDocument->documentJob, fmtIdx);
- err = GXGetJobError(whichDocument->documentJob);
- }
- else
- theFormat = nil;
-
- if (!err)
- whichDocument->pageFormat[pg] = theFormat;
- }
-
- DisposHandle(theFormatIdxList);
- }
-
- return err;
- }
-
-
- /************************************************************
- MySavePrintInfo - This routine saves our print record or
- gxJob data with the document, so that it can be used the
- next time the document is opened.
-
- *************************************************************/
-
- OSErr MySavePrintInfo(MyDocumentPtr whichDocument, short resRefNum)
- {
- OSErr err;
- Handle thePrintData, oldPrintData;
- OSType dataResType;
- short dataResID;
-
- /*
- If QuickDraw GX is present, flatten the document's gxJob into a
- handle so that we can write it to disk. Otherwise, make a copy
- of the document's print handle. In either case, set up the
- resource type and ID to use. Notice how smoothly this makes
- the rest of the routine work. We don't need to worry about
- whether we're storing a gxJob or a print record to disk.
- */
-
- UseResFile(resRefNum);
-
- if (gGXIsPresent)
- {
- err = MySaveFormatRefs(whichDocument);
- nrequire(err, CouldNotSaveFormatRefs);
-
- thePrintData = NewHandle(0);
- GXFlattenJobToHdl(whichDocument->documentJob, thePrintData);
- err = GXGetJobError(whichDocument->documentJob);
- nrequire(err, CouldNotFlattenJob);
-
- dataResType = kMyJobType;
- dataResID = kMyJobID;
- }
- else
- {
- thePrintData = (Handle) whichDocument->documentPrintHdl;
- err = HandToHand(&thePrintData);
- nrequire(err, CouldNotDuplicatePrintHdl);
-
- dataResType = kMyPrintRecType;
- dataResID = kMyPrintRecID;
- }
-
- // If there's an existing resource, delete it.
-
- oldPrintData = Get1Resource(dataResType, dataResID);
-
- if (oldPrintData != nil)
- {
- RmveResource(oldPrintData);
- UpdateResFile(resRefNum);
- DisposHandle(oldPrintData);
- }
-
- // Add our new resource.
-
- AddResource(thePrintData, dataResType, dataResID, "\p");
- err = ResError();
- nrequire(err, CouldNotAddResource);
-
- WriteResource(thePrintData);
- UpdateResFile(resRefNum);
- DetachResource(thePrintData);
-
- CouldNotAddResource:
- DisposHandle(thePrintData);
-
- CouldNotDuplicatePrintHdl:
- CouldNotFlattenJob:
- CouldNotSaveFormatRefs:
- return err;
- }
-
-
- /************************************************************
- MyLoadPrintInfo - This routine loads our document's
- previously saved print record or gxJob data.
-
- *************************************************************/
-
- OSErr MyLoadPrintInfo(MyDocumentPtr whichDocument, short resRefNum)
- {
- OSErr err = noErr;
- THPrint savedPrintHdl;
- Handle theJobData = nil;
-
- UseResFile(resRefNum);
-
- /*
- If we're using QuickDraw GX, and there's a job resource saved,
- load it and unflatten it. Any format references saved with a
- document are no longer valid, so we need to adjust them.
- */
- if (gGXIsPresent)
- {
- theJobData = Get1Resource(kMyJobType, kMyJobID);
-
- if (theJobData != nil)
- {
- GXUnflattenJobFromHdl(whichDocument->documentJob, theJobData);
- err = GXGetJobError(whichDocument->documentJob);
- ReleaseResource(theJobData);
-
- if (err == noErr)
- err = MyAdjustFormats(whichDocument);
- }
- }
-
- /*
- If there was no job data saved or we're not using QuickDraw GX,
- try to load a previously saved print record. If we find one,
- and QuickDraw GX is being used, convert the print record to
- a gxJob. Otherwise, if we find one and QuickDraw GX is not
- being used, dispose of the print handle that we allocated in
- our MyCreateDocument routine.
-
- Note that if no gxJob or print record was previously saved,
- we'll simply end up using the one we created in our
- MyCreateDocument routine. So, no matter what, we'll always
- have a gxJob or print record to use!
- */
- if (theJobData == nil)
- {
- savedPrintHdl = (THPrint) Get1Resource(kMyPrintRecType, kMyPrintRecID);
-
- if (savedPrintHdl != nil)
- {
- DetachResource((Handle) savedPrintHdl);
-
- if (gGXIsPresent)
- {
- GXConvertPrintRecord(whichDocument->documentJob, savedPrintHdl);
- DisposeHandle((Handle) savedPrintHdl);
- err = GXGetJobError(whichDocument->documentJob);
- }
- else
- {
- DisposHandle((Handle) whichDocument->documentPrintHdl);
- whichDocument->documentPrintHdl = savedPrintHdl;
- }
- }
- }
-
- return err;
- }
-
-
-
-
-